
import UIKit

public class CycleScrollView: UIView {
    override public init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    @available(*, unavailable)
    public required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = frame.size
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        layout.scrollDirection = .horizontal
        layout.sectionInset = .zero
        let lazy = UICollectionView(frame: bounds, collectionViewLayout: layout)
        lazy.register(CycleScrollViewCell.self, forCellWithReuseIdentifier: "CycleScrollViewCell")
        lazy.backgroundColor = .clear
        lazy.alwaysBounceVertical = false
        lazy.alwaysBounceHorizontal = true
        lazy.isPagingEnabled = true
        lazy.dataSource = self
        lazy.delegate = self
        lazy.showsVerticalScrollIndicator = false
        lazy.showsHorizontalScrollIndicator = false
        return lazy
    }()

    var numberOfPages: Int = 0
    
    var currentPage: Int = 0

    public weak var dataSource: CycleScrollViewDataSource?

    public weak var delegate: CycleScrollViewDelegate?

    public var pageDotColor: UIColor? = .lightGray

    public var currentPageDotColor: UIColor? = .black

    public var dotSize: CGSize = .init(width: 8, height: 8)

    public var dotSpace: CGFloat = 8

    public var dotCornerRadius: CGFloat = 4

    public var pageControlBottomOffset: CGFloat = 10
    /// 循环的次数, 设置 1 单次循环
    public var repeatCount: Int = 1000

    override public func layoutSubviews() {
        super.layoutSubviews()
        collectionView.frame = bounds
    }

    lazy var pageControl: PageControl = {
        let lazy = PageControl(frame: .zero)
        lazy.pageDotColor = .lightGray
        lazy.currentPageDotColor = .black
        return lazy
    }()
}

public extension CycleScrollView {
    func reloadData() {
        numberOfPages = dataSource?.numberOfItems(in: self) ?? 0
        collectionView.reloadData()
        currentPage = 300 * numberOfPages
        if repeatCount == 1 {
            currentPage = 0
        } else {
            currentPage = 300 * numberOfPages
        }

        if numberOfPages > 1 {
            let offsetY = frame.height - pageControlBottomOffset - dotSize.height
            pageControl.frame = CGRect(x: 0, y: offsetY, width: frame.width, height: dotSize.height)
            pageControl.currentPageDotColor = currentPageDotColor
            pageControl.pageDotColor = pageDotColor
            pageControl.dotSize = dotSize
            pageControl.dotSpace = dotSpace
            pageControl.dotCornerRadius = dotCornerRadius
            pageControl.numberOfPages = numberOfPages
            pageControl.currentPage = 0
            pageControl.isHidden = false
        } else {
            pageControl.isHidden = true
        }

        collectionView.scrollToItem(at: .init(row: currentPage, section: 0), at: .centeredHorizontally, animated: false)
    }
}

extension CycleScrollView {
    func setup() {
        addSubview(collectionView)
        addSubview(pageControl)
    }
}

extension CycleScrollView: UICollectionViewDataSource {
    public func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
        numberOfPages * repeatCount
    }

    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CycleScrollViewCell", for: indexPath)
        if let cell2 = cell as? CycleScrollViewCell {
            let index = indexPath.row % numberOfPages
            dataSource?.cycleScrollView(self, display: cell2, at: index)
        }
        return cell
    }
}

extension CycleScrollView: UICollectionViewDelegate {
    public func collectionView(_: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let index = indexPath.row % numberOfPages
        delegate?.cycleScrollView(self, didSelectItemAt: index)
    }

    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView.isTracking || scrollView.isDecelerating {
            let width: CGFloat = frame.width
            if scrollView.contentOffset.x.truncatingRemainder(dividingBy: width) == 0 {
                currentPage = Int(scrollView.contentOffset.x / width)
                pageControl.currentPage = currentPage % numberOfPages

                if repeatCount > 1000 {
                    if currentPage > (900 * numberOfPages) {
                        currentPage = 300 * numberOfPages + pageControl.currentPage
                        collectionView.scrollToItem(at: .init(row: currentPage, section: 0), at: .centeredHorizontally, animated: false)
                    }

                    if currentPage < (100 * numberOfPages) {
                        currentPage = 300 * numberOfPages + pageControl.currentPage
                        collectionView.scrollToItem(at: .init(row: currentPage, section: 0), at: .centeredHorizontally, animated: false)
                    }
                }
            }
        }
    }
}

public protocol CycleScrollViewDataSource: AnyObject {
    func numberOfItems(in cycleScrollView: CycleScrollView) -> Int
    func cycleScrollView(_ cycleScrollView: CycleScrollView, display cell: CycleScrollViewCell, at index: Int)
}

public protocol CycleScrollViewDelegate: AnyObject {
    func cycleScrollView(_ cycleScrollView: CycleScrollView, didSelectItemAt index: Int)
}
